package com.espirit.eap.manager;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.opensymphony.xwork2.LocaleProvider;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.TextProviderSupport;
import com.opensymphony.xwork2.util.ValueStack;

/**
 * Read i18n data from database, don't use the struts properties
 * 
 * @author feng Date: 2009-5-1
 */
@Component
@Scope("prototype")
public class I18nTextProvider extends TextProviderSupport implements TextProvider {

	@Resource
	private I18nService i18nService;

	private LocaleProvider localeProvider;
	
	private static final Map messageFormats = new HashMap();
	
	@Override
    public void setLocaleProvider(LocaleProvider localeProvider) {
        this.localeProvider = localeProvider;
        super.setLocaleProvider(localeProvider);
    }
	
	
	// priority: locale --> current name --> key as the name
	public String getTextByLocale(String key, String locale, boolean useKyeAsDefaultValue) {
		if (key == null || key.length() == 0) {
			return "";
		}
		
		I18n i18n = i18nService.getI18n(key);
		if (i18n.getId() == null) {
			// load the combine key automatically
			int index = key.indexOf(':');
			if (index > 0) {
				String prefix = key.substring(0, index);
				String suffix = key.substring(index + 1);
				return getTextByLocale(prefix, locale, useKyeAsDefaultValue)
						+ ':' + getTextByLocale(suffix, locale, useKyeAsDefaultValue);
			}
		}
		
		String text;
        if (i18n.getId() != null) {
        	if (locale == null || locale.length() == 0) {
        		text = i18n.getNameByLocale(localeProvider.getLocale());
        	} else {
        		text = i18n.getNameByLocale(locale);
        	}
        } else {
        	// format the key
        	int index = key.lastIndexOf('.');
        	String suffix = key.substring(index + 1);
        	if (NumberUtils.isDigits(suffix)) {
        		int begin = key.lastIndexOf('.', index - 1);
        		suffix = key.substring(begin + 1, index);
        	}
        	text = suffix;
        }
        
        if (text == null) {
			if (locale != null && locale.length() > 0) {		// current locale
				text = getTextByLocale(key, null, false);
			}
		}
        
        if (useKyeAsDefaultValue && text == null) {
        	text = i18n.convertKeyToName();
        }
        return text;
    }
	
	@Override
	public String getText(String key, String defaultValue, List args) {
        I18n i18n = i18nService.getI18n(key);
        String text;
        if (i18n.getId() != null) {
        	text = i18n.getNameByLocale(localeProvider.getLocale());
        } else {
        	text = i18n.getCnName() + (args == null ? "" : ": " + StringUtils.join(args, ','));
        }
        return text;
    }
	
	@Override
	public String getText(String key, String defaultValue, String[] args) {
		I18n i18n = i18nService.getI18n(key);
        String text;
        if (i18n.getId() != null) {
        	text = i18n.getNameByLocale(localeProvider.getLocale());
        	if (text != null) {
        		if (args != null && args.length > 0) {
		        	MessageFormat mf = buildMessageFormat(text, localeProvider.getLocale());
		            return mf.format(args);
        		}
        	} else {
        		text = key;
        	}
        } else {
        	if (args == null || args.length == 0) {
        		text = i18n.getCnName();
        	} else {
        		text = i18n.getCnName() + ": " + StringUtils.join(args, ',');
        	}
        }
        return text;
	}
	
	private static MessageFormat buildMessageFormat(String pattern, Locale locale) {
        MessageFormatKey key = new MessageFormatKey(pattern, locale);
        MessageFormat format = null;
        format = (MessageFormat) messageFormats.get(key);
        if (format == null) {
            format = new MessageFormat(pattern);
            format.setLocale(locale);
            format.applyPattern(pattern);
            messageFormats.put(key, format);
        }

        return format;
    }
	
	@Override
	public String getText(String key, String defaultValue, List args, ValueStack stack) {
		I18n i18n = i18nService.getI18n(key);
        String text;
        if (i18n.getId() != null) {
        	text = i18n.getNameByLocale(localeProvider.getLocale());
        } else {
        	text = i18n.getCnName() + (args == null ? "" : ": " + StringUtils.join(args, ','));
        }
        return text;
	}
	
	@Override
	public String getText(String key, String defaultValue, String[] args, ValueStack stack) {
		I18n i18n = i18nService.getI18n(key);
        String text;
        if (i18n.getId() != null) {
        	text = i18n.getNameByLocale(localeProvider.getLocale());
        } else {
        	text = i18n.getCnName() + (args == null ? "" : ": " + StringUtils.join(args, ','));
        }
        return text;
	}
	
	@Override
	public boolean hasKey(String key) {
		return true;
	}
	
	static class MessageFormatKey {
        String pattern;
        Locale locale;

        MessageFormatKey(String pattern, Locale locale) {
            this.pattern = pattern;
            this.locale = locale;
        }

        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof MessageFormatKey)) return false;

            final MessageFormatKey messageFormatKey = (MessageFormatKey) o;

            if (locale != null ? !locale.equals(messageFormatKey.locale) : messageFormatKey.locale != null)
                return false;
            if (pattern != null ? !pattern.equals(messageFormatKey.pattern) : messageFormatKey.pattern != null)
                return false;

            return true;
        }

        public int hashCode() {
            int result;
            result = (pattern != null ? pattern.hashCode() : 0);
            result = 29 * result + (locale != null ? locale.hashCode() : 0);
            return result;
        }
    }
}
